#include "TransAppender.h"

#include "Topic.h"
#include "Record.h"
#include "Log.h"

using recovery::Status;

TransAppender::TransAppender(Topic & topic)
:_log(ILog::create(topic))
{
}
TransAppender::~TransAppender(){
}
bool TransAppender::open(){
	return _log->open();
}
bool TransAppender::close(){
	return _log->close();
}

TransAppender::Transaction::Transaction(std::unique_ptr<ILog> & log)
: _log(log)
{
    _lastTransOffset = _log->meta().logSize().value();
}
TransAppender::Transaction::~Transaction(){
}


std::unique_ptr<TransAppender::Transaction> TransAppender::transaction(){
	return std::unique_ptr<Transaction>(new Transaction(_log));
}



TransAppender::Transaction & TransAppender::Transaction::execute(long long key, const std::string & value){
    switch(_status) {
        case TRANS_STATUS_READY :
            _status = TRANS_STATUS_EXECUTING;
            break;
        case TRANS_STATUS_EXECUTING :
            break;
        default:
            break;
    }

	Record record(key, value);
    size_t appendBytes =  0;
	auto s = _log->append(record.str(), appendBytes);
	if(s){
		_size = appendBytes;
        _log->meta().logSize().advance(_size); //no sync, meta.synced == false
	}
	else{
		//TODO: error log ......
	}
	return *this;
}
recovery::Status TransAppender::Transaction::commit(){
    switch(_status){
        case TRANS_STATUS_EXECUTING : {
            auto s = _log->sync();
            if(!s) return s;

            s = _log->meta().sync();  // logsize().value()->sync to meta
            if(!s) return s;

            _status = TRANS_STATUS_READY;
            return s;
        }

        case TRANS_STATUS_READY : {
            return Status::OK();
        }
        default:
            break;
    }
	return Status::Failed("Status is failed!");
}
recovery::Status TransAppender::Transaction::rollback() {
    auto s = Status::OK();

    switch(_status){
        case TRANS_STATUS_READY :
            return s;

        case TRANS_STATUS_EXECUTING : {
            if(_lastTransOffset < _log->meta().logSize().value()){
                s = _log->truncate(_lastTransOffset);
                if(!s) return s;
                _log->meta().logSize().overwrite(_lastTransOffset);
            }
            else if(_lastTransOffset > _log->meta().logSize().value()){
                s = Status::Failed("lastTransOffset is error!, bigger than the real content");         
            }
            else{// _lastTransOffset == _log->meta().logSize().value()
                // no change
            }

            _status = TRANS_STATUS_READY;
            return s;
        }
        default:
            break;
    }
    return Status::Failed("Status failed");
}

